#include<iostream>
#include<sys/mman.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
using namespace std;

#define handle_err(msg) \
        do {perror(msg); exit(EXIT_FAILURE);} while(0)

//从指定文件的指定偏移量开始-读取部分或全部内容并输出到标准输出
//open,fstat,mmap,write
int main(int argc,char*argv[])
{
    char *addr;
    int fd;
    struct stat sb;
    off_t offset,pa_offset;
    size_t length;
    ssize_t s;
    //需要传入3-4个命令行参数
    //argv[0]程序名，argv[1]待读取的文件名,argv[2]文件内的读取起始的偏移量（转为数值），argv[3]期望读取的长度
    if(argc<3 ||argc>4)
    {
        fprintf(stderr,"%s file offset [length]\n",argv[0]);
        exit(EXIT_FAILURE);
    }
    //文件的打开和状态获取
    fd=open(argv[1],O_RDONLY);
    if(fd==-1)
        handle_err("open");
    //获取文件元数据，用于检验偏移量的合法性
    if(fstat(fd,&sb)==-1)
    {
        handle_err("fstat");
    }
    //作为文件内的逻辑读取起始位置
    offset=atoi(argv[2]);
    //它通过位运算将offset对齐到系统页大小-满足mmap对文件偏移量虚页对齐的要求
    pa_offset=offset& ~(sysconf(_SC_PAGE_SIZE)-1);
    if(offset >=sb.st_size)
    {
        fprintf(stderr,"offset is past end of file\n");
        exit(EXIT_FAILURE);
    }
    //读取长度计算
    if(argc==4)
    {
        //尝试读取期望长度
        length=atoi(argv[3]);
        if(length+offset>sb.st_size)
        {
        length=sb.st_size-offset;
        }
        else
        {
            length=sb.st_size-offset;
        }
    }
    //内存映射与数据输出
    addr=(char*)mmap(NULL,length+offset-pa_offset,PROT_READ,MAP_PRIVATE,fd,pa_offset);
    if(addr==MAP_FAILED)
    {
        handle_err("mmap");
    }
    //标准输出的文件描述符
    s=write(STDOUT_FILENO,addr+offset-pa_offset,length);
    if(s!=length)
    {
        if(s==-1)
        {
            handle_err("write");
        }
        fprintf(stderr,"partial write");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);

    return 0;
}